package com.pro.common.modules.service.dependencies.config.component;

import cn.hutool.core.util.StrUtil;
import com.pro.common.api.R;
import com.pro.common.modules.service.dependencies.util.IPUtils;
import com.pro.framework.api.FrameworkConst;
import com.pro.framework.api.enums.EnumEnv;
import com.pro.common.api.exception.BusinessNoTranslateException;
import com.pro.common.api.exception.BusinessPosterException;
import com.pro.common.api.model.IResponse;
import com.pro.common.service.util.I18nUtils;
import com.pro.common.service.properties.BaseProperties;
import com.pro.framework.api.model.IFrameworkException;
import com.pro.framework.api.model.ThirdException;
import com.pro.framework.api.util.ExceptionUtil;
import feign.RetryableException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;
import org.springframework.web.servlet.resource.NoResourceFoundException;

import java.io.IOException;
import java.net.SocketException;
import java.sql.SQLException;

/**
 * 异常处理器
 */
@RestControllerAdvice
@Slf4j
@AllArgsConstructor
public class BaseExceptionHandler {


    private BaseProperties baseProperties;

    public static String getErrMsg(String lang, Exception e) {
        if (e instanceof IFrameworkException ei && !(e instanceof BusinessNoTranslateException)) {
            return I18nUtils.getByLang(lang, ei.getMsg(), ei.getParams());
        }
        return e.getMessage();
    }

    //    public BaseExceptionHandler(CommonProperties commonProperties) {
//        this.commonProperties = commonProperties;
//    }
//    @ExceptionHandler(NoHandlerFoundException.class)
//    @ResponseBody
//    public R handleNotFound2(NoHandlerFoundException e) {
//        return R.fail(40401, "接口不存在: " + e.getRequestURL());
//    }
    @ExceptionHandler(NoHandlerFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public IResponse<?, ?> handleNotFound(NoHandlerFoundException ex) {
        return R.fail(40401, "Resource not found");
    }

    @ExceptionHandler(NoResourceFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public IResponse<?, ?> handleNotFound(NoResourceFoundException ex) {
        return R.fail(40401, "Resource not found");
    }

    @ExceptionHandler(IOException.class)
    public void handleIOException(HttpServletRequest request, HttpServletResponse response, IOException e) {
        String exceptionClass = e.getClass().getSimpleName();
        String message = e.getMessage();
        if (e instanceof SocketException
                || (message != null && (message.contains("Broken pipe")
                || message.contains("Connection reset by peer")))) {
            log.info("客户端中断连接 {}: {} - {}", exceptionClass, request.getRequestURI(), message);
            return; // ✅ 不再尝试输出任何内容
        } else {
            log.warn("{}: {} - {}", exceptionClass, request.getRequestURI(), message, e);
            // 只在非 Broken pipe 时才尝试写入
            if (!response.isCommitted()) {
                try {
                    response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
                    response.setContentType("application/json;charset=UTF-8");
                    response.getWriter().write("{\"code\":500,\"msg\":\"" + message + "\"}");
                } catch (IOException ex) {
                    log.error("写入 response 失败: {}", ex.getMessage());
                }
            }
        }
    }


//    @ExceptionHandler(FeignException.class)
//    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
//    public IResponse<?, ?> handleFeignException(HttpServletRequest request, FeignException ex) {
//        String uri = request.getRequestURI();
//
//        // 尝试解析返回体（一般是 JSON）
//        String body = ex.contentUTF8();
//        String simpleMsg = ex.getMessage();
//
//        log.error("Feign 调用失败 -> [{} {}], status={}, body={}",
//                request.getMethod(), uri, ex.status(), body);
//
//        // 业务上可以只返回简化信息
//        return R.fail(50001, "远程调用失败: " + simpleMsg);
//    }

    @ExceptionHandler(RetryableException.class)
    @ResponseStatus(HttpStatus.REQUEST_TIMEOUT)
    public IResponse<?, ?> handleRetryableException(HttpServletRequest request, RetryableException ex) {
        log.error("Feign 请求超时 -> [{} {}], message={}",
                request.getMethod(), request.getRequestURI(), ex.getMessage());
        return R.fail(408, "请求超时，请稍后重试");
    }

    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) // 500 错误
    public IResponse<?, ?> handleException(HttpServletRequest request, Exception e) {
        boolean isDev = !EnumEnv.prod.equals(baseProperties.getCommonEnv());
        Throwable cause = null == e.getCause() ? e : e.getCause();
        String exceptionClass = e.getClass().getSimpleName();
//        String exceptionClassOri = e.getClass().getSimpleName();
        Integer code = 500;
        String msg = cause.getMessage();
//        Throwable cause = e.getCause();
//        if (cause != null) {
//            msg = cause.getMessage();
//        }
//        else {
        switch (exceptionClass) {
            case "NoResourceFoundException":
                log.warn("{} {} {}", exceptionClass, request.getRequestURI(), msg);
            case "SQLException":
            case "UncategorizedSQLException":
                boolean readOnly = false;
                if (cause instanceof SQLException) {
                    if ("The MySQL server is running with the --super-read-only option so it cannot execute this statement".equals(
                            msg)) {
                        readOnly = true;
                    }
                }
                if (readOnly) {
                    String errorMsg = "可能数据库满了_需要扩容";
                    msg = I18nUtils.get(errorMsg) + " | " + msg;
                    log.error("{} {} {} {}", exceptionClass, request.getRequestURI(), errorMsg, msg);
                } else {
                    log.error("{} {} {}", exceptionClass, request.getRequestURI(), msg, e);
                }
                break;
            case "MultiException":
            case "BusinessException":
            case "BusinessPosterException":
            case "BusinessNeedRetryException":
            case "FrameworkException":
                if ((((IFrameworkException) e)).getCode() == 403) {
                    String ipAddress = IPUtils.getIpAddress(request);
                    log.warn("{} {}  ip:{} {}", exceptionClass, request.getRequestURI(), ipAddress, msg);
                } else {
                    log.warn("{} {} {}", exceptionClass, request.getRequestURI(), ExceptionUtil.logThrowable(e,isDev?true:null));
                }
                return this.translate((IFrameworkException) e);
            case "ThirdException":
                if (e instanceof ThirdException ex) {
                    switch (ex.getType()) {
                        case AUTH_EXPIRED -> {}
                        case RETRYABLE -> {}
                        case TIMEOUT -> {}
                        case IGNORED -> {}
                        case UNKNOWN -> {}
                        default  -> {}
                    }
                    log.warn("{} {} {}", exceptionClass, request.getRequestURI(), ExceptionUtil.logThrowable(e,null));
                    return this.translate(ex);
                }
            case "BusinessBreakException":
                log.warn("{} {} {}", exceptionClass, request.getRequestURI(), ExceptionUtil.logThrowable(e,isDev?true:null));
                return this.translate((IFrameworkException) e);
            case "HttpTimeoutException":
                log.info("{} {} {}", exceptionClass, request.getRequestURI(), ExceptionUtil.logThrowable(e, false));
                return R.fail(500, msg);
            case "BusinessNoTranslateException":
                log.info("{} {} {}", exceptionClass, request.getRequestURI(), ExceptionUtil.logThrowable(e, false));
                return this.noTranslate((BusinessNoTranslateException) e);
            case "HttpRequestMethodNotSupportedException":
                log.warn("{} {} {}", exceptionClass, request.getRequestURI(), ExceptionUtil.logThrowable(e, false));
                return this.noTranslate((BusinessNoTranslateException) e);
            case "SQLIntegrityConstraintViolationException":
            case "DuplicateKeyException":
            case "DataIntegrityViolationException":
                if (msg.startsWith("Duplicate entry ")) {
                    msg = I18nUtils.get("数据已存在_无需重复添加") + " " + msg;
                    log.warn("{} {} {} {}", exceptionClass, request.getRequestURI(), msg, msg);
                }
//                log.warn("{} {} {} {}", exceptionClass, request.getRequestURI(), msg, ExceptionUtil.logThrowable(e, false));
//                log.warn("{} {} {} {}", exceptionClass, request.getRequestURI(), msg, ExceptionUtil.logThrowable(e, false));
//                break;
//                msg = cause.getMessage();
                else if ("Field 'user_id' doesn't have a default value".equals(msg)) {
                    msg = I18nUtils.get("请先选择用户");
                    log.warn("{} {} {} {}", exceptionClass, request.getRequestURI(), msg, ExceptionUtil.logThrowable(e, false, msg));
                }
                else if (msg.startsWith("Data truncation: Data too long for column")) {
                    String[] split = msg.split("'");
                    msg = I18nUtils.get("输入的数据_长度超过该字段的最大限制", split[1]);
                    log.warn("{} {} {} {}", exceptionClass, request.getRequestURI(), msg, ExceptionUtil.logThrowable(e, false, msg));
                }
                break;
            default:
                if (EnumEnv.prod.equals(baseProperties.getCommonEnv())) {
                    msg = I18nUtils.get("未知的异常_请联系管理员");
                }
                log.error("{} {} {}", exceptionClass, request.getRequestURI(),
                        StrUtil.nullToDefault(msg, FrameworkConst.Str.EMPTY), e);
                break;
        }
        // 没太大意义，但是加了会不方便后期的一些截图，先去掉
//        if (EnumEnv.dev.equals(baseProperties.getCommonEnv())) {
//            msg = exceptionClass + " " + msg;
////            log.debug("{} {} {}", exceptionClass, msg, request.getRequestURI(), e);
//        }
        return R.fail(code, msg);
    }

    private IResponse<?, ?> translate(IFrameworkException e) {
        //新增不翻译
//        String rawMsg = e.getMsg();
//        if (rawMsg != null && rawMsg.startsWith("@@NO_TRANSLATE@@")) {
//            // 直接去掉前缀返回原文，不翻译
//            String errorMsg = rawMsg.substring("@@NO_TRANSLATE@@".length());
//            return R.fail(e.getCode(), errorMsg);
//        }

        String errorMsg = I18nUtils.get(e.getMsg(), e.getParams());
        if (StrUtil.isBlank(errorMsg)) {
//            if (!EnumEnv.prod.equals(baseProperties.getCommonEnv())) {
//                errorMsg = e.getMsg();
//            }
//            switch (commonProperties.getApplication()) {
//                case admin:
//                    if (EnumAuthDict.ADMIN_LANG_CHINESE.getValueCacheOrDefault(true)) {
//                        errorMsg = e.getMsg();
//                    }
//                    break;
//            }
        }
        R<?> rs = R.fail(e.getCode(), errorMsg);
        if (e instanceof BusinessPosterException) {
            rs.setPosterCode(((BusinessPosterException) e).getPosterCode());
            rs.setParamMap(((BusinessPosterException) e).getParamMap());
        }
        return rs;
    }

    private IResponse<?, ?> noTranslate(BusinessNoTranslateException e) {
        return R.fail(e.getCode(), e.getMsg());
    }

}
